package com.guomei.utils;

import com.guomei.configs.JwtConfig;
import com.guomei.constans.RedisConstants;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Slf4j
@Component
public class JwtUtil {
    @Autowired
    private JwtConfig jwtConfig;

    @Autowired
    private RedisUtils redisUtils;

    private static JwtConfig staticJwtConfig;
    private static RedisUtils staticRedisUtils;

    @PostConstruct
    public void init() {
        staticJwtConfig = this.jwtConfig;
        staticRedisUtils = this.redisUtils;
    }

    /**
     * 生成accessToken
     */
    public static String generateAccessToken(Long userId, String username) {
        String tokenId = UUID.randomUUID().toString().replace("-", "");
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        claims.put("username", username);
        claims.put("tokenId", tokenId);
        claims.put("type", "access");

        String token = generateToken(claims, staticJwtConfig.getAccessTokenExpiration());

        // 将token存储到Redis中
        String redisKey = RedisConstants.ACCESS_TOKEN_PREFIX + tokenId;
        Map<String, Object> tokenInfo = new HashMap<>();
        tokenInfo.put("userId", userId);
        tokenInfo.put("username", username);
        tokenInfo.put("createTime", System.currentTimeMillis());

        staticRedisUtils.set(redisKey, tokenInfo, staticJwtConfig.getAccessTokenExpiration());
        log.info("生成accessToken，存入redisKey：{}，value：{}", redisKey, tokenInfo);

        return token;
    }

    /**
     * 生成refreshToken
     */
    public static String generateRefreshToken(Long userId, String username) {
        String tokenId = UUID.randomUUID().toString().replace("-", "");
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        claims.put("username", username);
        claims.put("tokenId", tokenId);
        claims.put("type", "refresh");

        String token = generateToken(claims, staticJwtConfig.getRefreshTokenExpiration());

        // 将refreshToken存储到Redis中
        String redisKey = RedisConstants.REFRESH_TOKEN_PREFIX + tokenId;
        Map<String, Object> tokenInfo = new HashMap<>();
        tokenInfo.put("userId", userId);
        tokenInfo.put("username", username);
        tokenInfo.put("createTime", System.currentTimeMillis());

        staticRedisUtils.set(redisKey, tokenInfo, staticJwtConfig.getRefreshTokenExpiration());
        log.info("生成refreshToken，存入redisKey：{}，value：{}", redisKey, tokenInfo);
        return token;
    }

    /**
     * 生成token
     */
    private static String generateToken(Map<String, Object> claims, Long expiration) {
        Date expirationDate = new Date(System.currentTimeMillis() + expiration * 1000);
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS512, staticJwtConfig.getSecret())
                .compact();
    }

    /**
     * 解析token
     */
    public static Claims parseToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(staticJwtConfig.getSecret())
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            log.error("解析token失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 验证token是否有效（通过Redis验证）
     */
    public static boolean validateToken(String token) {
        try {
            Claims claims = parseToken(token);
            if (claims == null) {
                return false;
            }

            // 检查token是否过期
            if (claims.getExpiration().before(new Date())) {
                return false;
            }

            String tokenId = (String) claims.get("tokenId");
            String type = (String) claims.get("type");

            if (tokenId == null || type == null) {
                return false;
            }

            String redisKey = getTypePrefix(type) + tokenId;

            // 检查Redis中是否存在该token
            return staticRedisUtils.hasKey(redisKey);
        } catch (Exception e) {
            log.error("验证token失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 从token中获取用户ID
     */
    public static Long getUserIdFromToken(String token) {
        try {
            if (token == null || token.trim().isEmpty()) {
                log.warn("Token为空");
                return null;
            }
            
            Claims claims = parseToken(token);
            if (claims == null) {
                log.warn("Token解析失败，claims为null");
                return null;
            }
            
            if (isTokenExpired(claims)) {
                log.warn("Token已过期");
                return null;
            }
            
            String tokenId = (String) claims.get("tokenId");
            String type = (String) claims.get("type");
            log.debug("Token解析结果: tokenId={}, type={}", tokenId, type);

            if (tokenId != null && type != null) {
                String redisKey = getTypePrefix(type) + tokenId;
                @SuppressWarnings("unchecked")
                Map<String, Object> tokenInfo = (Map<String, Object>) staticRedisUtils.get(redisKey);
                if (tokenInfo != null) {
                    Object userIdObj = tokenInfo.get("userId");
                    if (userIdObj != null) {
                        return Long.valueOf(userIdObj.toString());
                    } else {
                        log.warn("Redis中token信息不包含userId: {}", tokenInfo);
                    }
                } else {
                    log.warn("Redis中未找到token信息: {}", redisKey);
                }
            } else {
                log.warn("Token中缺少tokenId或type: tokenId={}, type={}", tokenId, type);
            }
        } catch (Exception e) {
            log.error("从token中获取用户ID失败: token={}", token, e);
        }
        return null;
    }

    /**
     * 从token中获取用户名
     */
    public static String getUsernameFromToken(String token) {
        Claims claims = parseToken(token);
        if (claims != null && !isTokenExpired(claims)) {
            String tokenId = (String) claims.get("tokenId");
            String type = (String) claims.get("type");

            if (tokenId != null && type != null) {
                String redisKey = getTypePrefix(type) + tokenId;
                @SuppressWarnings("unchecked")
                Map<String, Object> tokenInfo = (Map<String, Object>) staticRedisUtils.get(redisKey);
                if (tokenInfo != null) {
                    return (String) tokenInfo.get("username");
                }
            }
        }
        return null;
    }

    /**
     * 判断token是否过期
     */
    private static boolean isTokenExpired(Claims claims) {
        return claims.getExpiration().before(new Date());
    }

    /**
     * 判断是否为refreshToken
     */
    public static boolean isRefreshToken(String token) {
        Claims claims = parseToken(token);
        if (claims != null) {
            String type = (String) claims.get("type");
            return "refresh".equals(type);
        }
        return false;
    }

    /**
     * 获取token类型前缀
     */
    private static String getTypePrefix(String type) {
        if ("access".equals(type)) {
            return RedisConstants.ACCESS_TOKEN_PREFIX;
        } else if ("refresh".equals(type)) {
            return RedisConstants.REFRESH_TOKEN_PREFIX;
        }
        return "";
    }

    /**
     * 删除token（用户登出时使用）
     */
    public static void removeToken(String token) {
        Claims claims = parseToken(token);
        if (claims != null) {
            String tokenId = (String) claims.get("tokenId");
            String type = (String) claims.get("type");

            if (tokenId != null && type != null) {
                String redisKey = getTypePrefix(type) + tokenId;
                staticRedisUtils.delete(redisKey);
            }
        }
    }
}
